#pragma once

#include <compl/candy.h>

/* 启用预取 */
#define PROMPT_PREFETCH_ENABLE

#ifdef RESUME_FPU
	#define RESUME_SSE
	#define RESUME_FPUCTRL
#endif

#define SETJMP_NORMAL_RESUME 

#define __BUF_MXCSR			uint32_t mxcsr;
#define __BUF_FPUCTL			uint16_t fpctl;
#define __SAVE_SSE(regptr, offset)	TO_STRING(stmxcsr offset ( regptr )\n)
#define __LOAD_SSE(regptr, offset)	TO_STRING(ldmxcsr offset ( regptr )\n)
#define __SAVE_FPUCTRL(regptr, offset)	TO_STRING(fnstcw  offset ( regptr )\n)
#define __LOAD_FPUCTRL(regptr, offset)	TO_STRING(fldcw   offset ( regptr )\n)

typedef struct resume_fpuctx{
	__BUF_MXCSR
	__BUF_FPUCTL
} resume_fpuctx_t;

#define ST_FPUCTX(fpubuf)		\
resume_fpuctx_t *__fpubuf = fpubuf;	\
asm (	__SAVE_SSE(%0, 0)		\
	__SAVE_FPUCTRL(%0, 4)		\
	:				\
	: "r"(__fpubuf)			\
	:)

#define LD_FPUCTX(fpubuf)		\
resume_fpuctx_t *__fpubuf = fpubuf;	\
asm (	__LOAD_SSE(%0, 0)		\
	__LOAD_FPUCTRL(%0, 4)		\
	:				\
	: "r"(__fpubuf)			\
	:)

#ifdef RESUME_SSE
	#define BUF_MXCSR			__BUF_MXCSR
	#define SAVE_SSE(regptr, offset)	__SAVE_SSE(regptr, offset)
	#define LOAD_SSE(regptr, offset)	__LOAD_SSE(regptr, offset)
#else
	#define BUF_MXCSR
	#define SAVE_SSE(regptr, offset)	
	#define LOAD_SSE(regptr, offset)
#endif

#ifdef RESUME_FPUCTRL
	#define BUF_FPUCTL			__BUF_FPUCTL
	#define SAVE_FPUCTRL(regptr, offset)	__SAVE_FPUCTRL(regptr, offset)
	#define LOAD_FPUCTRL(regptr, offset)	__LOAD_FPUCTRL(regptr, offset)
#else
	#define BUF_FPUCTL
	#define SAVE_FPUCTRL(regptr, offset)
	#define LOAD_FPUCTRL(regptr, offset)
#endif 

typedef struct cresume_buf{
	word_t rip;	/* 0 */
	word_t rbx;	/* 8 */
	word_t rsp;	/* 16 */
	word_t rbp;	/* 24 */
	word_t r12;	/* 32 */
	word_t r13;	/* 40 */
	word_t r14;	/* 48 */
	word_t r15;	/* 56 */
	BUF_MXCSR 	/* 64 */
	BUF_FPUCTL 	/* 68 */
	/* total: 72 */
} cresume_ctx_t;

#define RESUME_STCTX(reg)					\
		"movq	(%%rsp),	%%rax /* get rip */	\n\
		leaq	8 (%%rsp),	%%rcx /* get rsp */	\n\
		movq	%%rax,		0 ("#reg") /* save rip */\n\
		movq	%%rbx,		8 ("#reg")		\n\
		movq	%%rcx,		16 ("#reg")		\n\
		movq	%%rbp,		24 ("#reg")		\n\
		movq	%%r12,		32 ("#reg")		\n\
		movq	%%r13,		40 ("#reg")		\n\
		movq	%%r14,		48 ("#reg")		\n\
		movq	%%r15,		56 ("#reg")		\n"\
		SAVE_SSE(reg, 64)				\
		SAVE_FPUCTRL(reg, 68)				

#define RESUME_LDCTX(reg)					\
		"movq   0 ("#reg"),	%%rcx			\n\
		movq	8 ("#reg"),	%%rbx			\n\
		movq	16 ("#reg"),	%%rsp			\n\
		movq	24 ("#reg"),	%%rbp			\n\
		movq	32 ("#reg"),	%%r12			\n\
		movq	40 ("#reg"),	%%r13			\n\
		movq	48 ("#reg"),	%%r14			\n\
		movq	56 ("#reg"),	%%r15			\n"\
		LOAD_SSE(reg, 64)				\
		LOAD_FPUCTRL(reg, 68)				

/* 尝试优化函数调用? */
// #define __ssetjmp(buf)\
//	 asm ("  call to_here%=\n\t								 \
//		 to_here%=:\n\t\
//		 movq	(%%rsp), %%rax /* get rip */		\n\
//		 leaq	8 (%%rsp), %%rcx /* get rsp */	  \n\
//		 movq	%%rax,  0 (%0) /* save rip */	\n\
//		 movq	%%rbx,  8 (%0)				   \n\
//		 movq	%%rcx, 16 (%0)				   \n\
//		 movq	%%rbp, 24 (%0)				   \n\
//		 movq	%%r12, 32 (%0)				   \n\
//		 movq	%%r13, 40 (%0)				   \n\
//		 movq	%%r14, 48 (%0)				   \n\
//		 movq	%%r15, 56 (%0)				   \n"\
//		 SAVE_SSE									\
//		 SAVE_FPUCTRL								\
//		 "xor	%%rax, %%rax\n					  \
//		 ret\n"\
//	 : \
//	 : "r"(buf)\
//	 : "memory");

static MAY_UNUSED NAKED word_t __ssetjmp(MAY_UNUSED cresume_ctx_t *buf){
	asm ("							\n"\
		RESUME_STCTX(%%rdi)				\
		"xor	%%rax, %%rax\n				\
		ret\n						"
	:
	:
	:	"memory");
}

/* assert rax != 0 */
static MAY_UNUSED NAKED word_t __ctxsw(MAY_UNUSED cresume_ctx_t *buf, MAY_UNUSED cresume_ctx_t *next_buf){
	asm (	RESUME_STCTX(%%rdi)		\
		RESUME_LDCTX(%%rsi)		\
		"jmpq	*%%rcx\n"
	:
	:
	:	"memory");
}

#define __slongjmp(buf, val)		\
asm (	"movq	%0,		%%rdi\n"\
	"movq	%1,		%%rax\n"\
	RESUME_LDCTX(%%rdi)		\
	"jmpq	*%%rcx"			\
	:				\
	: "rm"(buf), "i"(val)		\
	: "memory"); 			\
UNREACHABLE()

/* 使用立即数, 优化程序跳转及预测 */
#define __slongjmp_const_jmp(buf, val, func)		\
asm (	"movq	%0,		%%rdi\n"		\
	"movq	%1,		%%rax\n"		\
	RESUME_LDCTX(%%rdi)				\
	"jmp	%2"					\
	:						\
	: "rm"(buf), "i"(val), "i"(func)		\
	: "memory");					\
UNREACHABLE()

/* 调用后不再返回，因此调用链关系破坏是允许的 */
#define __setstack_exec(func, arg, sp)			\
asm ("	movq %0,	%%rsp				\n\
	movq %1,	%%rdi				\n\
	movq %2,	%%rsi				\n\
	callq		*%%rsi"				\
	:						\
	: "rm"(sp), "r"(arg), "r"(func) 		\
	: "rdi", "rsi");				\
UNREACHABLE()

/* 把call的地址变成常量, 消除寄存器依赖, 优化流水线 */
#define __setstack_exec_const(func, arg, sp)		\
asm ("	movq %0,	%%rsp				\n\
	movq %1,	%%rdi				\n\
	call		%2"				\
	:						\
	: "rm"(sp), "r"(arg), "i"(func)			\
	: "rdi");					\
UNREACHABLE()

#define __setstack_exec2(func, arg0, arg1, sp)		\
asm ("	movq %0,	%%rsp				\n\
	movq %1,	%%rdi				\n\
	movq %2,	%%rsi				\n\
	movq %3,	%%rdx				\n\
	callq		*%%rdx"				\
	:						\
	: "rm"(sp), "r"(arg0), "r"(arg1), "r"(func)	\
	: "rdi", "rsi", "rdx");				\
UNREACHABLE()

/* 在编译器已知函数func的情况下，避免indirect call(依赖访存结果的call)，防止CPU流水线阻塞  */
#define __setstack_exec2_const(func, arg0, arg1, sp)	\
asm ("	movq %0,	%%rsp				\n\
	movq %1,	%%rdi				\n\
	movq %2,	%%rsi				\n\
	call 		%3"				\
	:						\
	: "rm"(sp), "r"(arg0), "r"(arg1), "i"(func) 	\
	: "rdi", "rsi");				\
UNREACHABLE()

/* CAN_EVAL判断是否能在编译时已知 */
#define setstack_exec2(func, arg0, arg1, sp)	do{				\
	if(CAN_EVAL(func)){							\
		__setstack_exec2_const(func, (word_t)arg0, (word_t)arg1, sp);	\
	}else{									\
		__setstack_exec2(func, (word_t)arg0, (word_t)arg1, sp);		\
	}									\
}while(0)

#define setstack_exec(func, arg, sp)		do{				\
	if(CAN_EVAL(func)){							\
		__setstack_exec_const(func, (word_t)arg, sp);			\
	}else{									\
		__setstack_exec(func, (word_t)arg, sp);				\
	}									\
}while(0)


#ifdef SETJMP_NORMAL_RESUME
	typedef cresume_ctx_t fresume_t;
	#define SETJMP(jmpbuf)			__ssetjmp(jmpbuf)
	#define LONGJMP(jmpbuf, val)		do{					\
		SASSERT((val) == 1);							\
		fresume_t *__tmpres = (jmpbuf);						\
		if (CAN_EVAL(__tmpres->rip)) {						\
			__slongjmp_const_jmp(__tmpres, (word_t)val, __tmpres->rip);	\
		}									\
		else {									\
			__slongjmp(__tmpres, (word_t)val);				\
		}									\
	}while(0)
#else
	/* 使用builtin_setjmp和longjmp(理论上性能和稳定性可能更好(如果编译器有优化)) */
	#define SETJMP(jmpbuf)					\
	({ fresume_t *__tmpres; SASSERT(IS_SAMETYPE(__tmpres, (jmpbuf))); __builtin_setjmp((void **)(jmpbuf));})
	/* val只能传1 */
	#define LONGJMP(jmpbuf, val)				do{	\
	SASSERT((val) == 1);						\
	fresume_t *__tmpres; SASSERT(IS_SAMETYPE(__tmpres, (jmpbuf)));	\
	__builtin_longjmp((void **)jmpbuf, val); UNREACHABLE(); }	\
	while(0)
	
	typedef struct fresume_buf{
		word_t rbp;
		word_t rip;
#ifdef __SSP__
		word_t shadow_rsp;
#endif
		word_t rsp;
	} fresume_t;
#endif
